//SPDX-FileCopyrightText: Copyright 2022-2024 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

use crate::user_api::user_api_plugin::{decrypt_string, encrypt_string, get_user_id};
use tauri::{
    plugin::{Plugin, Result as PluginResult},
    AppHandle, Invoke, Manager, PageLoadPayload, Runtime, Window,
};
use tokio::fs;
use tokio::io::AsyncReadExt;
use tokio::io::AsyncWriteExt;
use tokio::sync::RwLock;

#[derive(Default)]
pub struct FileLock(pub RwLock<i32>);

#[derive(serde::Serialize, serde::Deserialize, Clone, PartialEq)]
pub struct UserDataSourceInfo {
    pub id: String,
    pub name: String,
    pub source_type: u32,
    pub source_info: String,
}

#[derive(serde::Serialize, serde::Deserialize, Clone, PartialEq)]
pub struct UserDataViewInfo {
    pub id: String,
    pub name: String,
    pub desc: String,
    pub bg_color: String,
}

async fn load_source_list<R: Runtime>(
    app_handle: AppHandle<R>,
) -> Result<Vec<UserDataSourceInfo>, String> {
    let _l = app_handle.state::<FileLock>().inner().0.read().await;

    let user_dir = crate::get_user_dir();
    if user_dir.is_none() {
        return Err("miss user dir".into());
    }
    let user_id = get_user_id(app_handle.clone()).await;
    if user_id == "" {
        return Err("must login".into());
    }
    let mut file_path = std::path::PathBuf::from(user_dir.unwrap());
    file_path.push(user_id);
    file_path.push("data_source.json");

    if !file_path.exists() {
        return Ok(Vec::new());
    }

    let f = fs::File::open(file_path).await;
    if f.is_err() {
        return Err(f.err().unwrap().to_string());
    }
    let mut f = f.unwrap();
    let mut data = Vec::new();
    let result = f.read_to_end(&mut data).await;
    if result.is_err() {
        return Err(result.err().unwrap().to_string());
    }
    let json_str = String::from_utf8(data);
    if json_str.is_err() {
        return Err(json_str.err().unwrap().to_string());
    }
    let json_str = json_str.unwrap();
    let server_list = serde_json::from_str(&json_str);
    if server_list.is_err() {
        return Err(server_list.err().unwrap().to_string());
    }
    let mut source_list: Vec<UserDataSourceInfo> = server_list.unwrap();
    for source in &mut source_list {
        let info = decrypt_string(app_handle.clone(), &source.source_info, false).await;
        if info.is_err() {
            return Err(info.err().unwrap().to_string());
        }
        source.source_info = info.unwrap();
    }
    return Ok(source_list);
}

async fn save_source_list<R: Runtime>(
    app_handle: AppHandle<R>,
    source_list: Vec<UserDataSourceInfo>,
) -> Result<(), String> {
    let _l = app_handle.state::<FileLock>().inner().0.write().await;

    let user_dir = crate::get_user_dir();
    if user_dir.is_none() {
        return Err("miss user dir".into());
    }
    let user_id = get_user_id(app_handle.clone()).await;
    if user_id == "" {
        return Err("must login".into());
    }
    let mut file_path = std::path::PathBuf::from(user_dir.unwrap());
    file_path.push(user_id);
    if !file_path.exists() {
        let result = fs::create_dir_all(&file_path).await;
        if result.is_err() {
            return Err(result.err().unwrap().to_string());
        }
    }
    file_path.push("data_source.json");

    let mut new_source_list = source_list.clone();
    for source in &mut new_source_list {
        let info = encrypt_string(app_handle.clone(), &source.source_info, false).await;
        if info.is_err() {
            return Err(info.err().unwrap().to_string());
        }
        source.source_info = info.unwrap();
    }

    let json_str = serde_json::to_string(&new_source_list);
    if json_str.is_err() {
        return Err(json_str.err().unwrap().to_string());
    }
    let json_str = json_str.unwrap();

    let f = fs::File::create(file_path).await;
    if f.is_err() {
        return Err(f.err().unwrap().to_string());
    }
    let mut f = f.unwrap();
    let result = f.write_all(json_str.as_bytes()).await;
    if result.is_err() {
        return Err(result.err().unwrap().to_string());
    }

    return Ok(());
}

#[tauri::command]
async fn add_source<R: Runtime>(
    app_handle: AppHandle<R>,
    source: UserDataSourceInfo,
) -> Result<(), String> {
    let tmp_list = load_source_list(app_handle.clone()).await;
    if tmp_list.is_err() {
        return Err(tmp_list.err().unwrap());
    }
    let tmp_list = tmp_list.unwrap();
    for cmp_source in &tmp_list {
        if cmp_source.id == source.id {
            return Ok(());
        }
    }

    let mut source_list = vec![source];
    source_list.extend(tmp_list);
    return save_source_list(app_handle, source_list).await;
}

#[tauri::command]
async fn update_source<R: Runtime>(
    app_handle: AppHandle<R>,
    source: UserDataSourceInfo,
) -> Result<(), String> {
    let tmp_list = load_source_list(app_handle.clone()).await;
    if tmp_list.is_err() {
        return Err(tmp_list.err().unwrap());
    }
    let tmp_list = tmp_list.unwrap();
    let mut new_source_list = Vec::new();
    for cmp_source in &tmp_list {
        let source_id = source.id.clone();
        if cmp_source.id == source_id {
            new_source_list.push(source.clone());
        } else {
            new_source_list.push(cmp_source.clone());
        }
    }

    return save_source_list(app_handle, new_source_list).await;
}

#[tauri::command]
async fn remove_source<R: Runtime>(app_handle: AppHandle<R>, id: String) -> Result<(), String> {
    let tmp_list = load_source_list(app_handle.clone()).await;
    if tmp_list.is_err() {
        return Err(tmp_list.err().unwrap());
    }
    let tmp_list = tmp_list.unwrap();
    let mut new_source_list = Vec::new();
    for cmp_source in &tmp_list {
        if cmp_source.id != id {
            new_source_list.push(cmp_source.clone());
        }
    }

    return save_source_list(app_handle, new_source_list).await;
}

#[tauri::command]
async fn list_source<R: Runtime>(
    app_handle: AppHandle<R>,
) -> Result<Vec<UserDataSourceInfo>, String> {
    return load_source_list(app_handle).await;
}

#[tauri::command]
async fn load_node_data<R: Runtime>(
    app_handle: AppHandle<R>,
    id: String,
) -> Result<String, String> {
    let user_dir = crate::get_user_dir();
    if user_dir.is_none() {
        return Err("miss user dir".into());
    }
    let user_id = get_user_id(app_handle.clone()).await;
    if user_id == "" {
        return Err("must login".into());
    }
    let file_name = format!("data_view_nodes_{}.json", id);
    let mut file_path = std::path::PathBuf::from(user_dir.unwrap());
    file_path.push(user_id);
    file_path.push(file_name);

    if !file_path.exists() {
        return Ok("[]".into());
    }

    let f = fs::File::open(file_path).await;
    if f.is_err() {
        return Err(f.err().unwrap().to_string());
    }
    let mut f = f.unwrap();
    let mut data = Vec::new();
    let result = f.read_to_end(&mut data).await;
    if result.is_err() {
        return Err(result.err().unwrap().to_string());
    }
    let json_str = String::from_utf8(data);
    if json_str.is_err() {
        return Err(json_str.err().unwrap().to_string());
    }
    let json_str = json_str.unwrap();
    return Ok(json_str);
}

#[tauri::command]
async fn save_node_data<R: Runtime>(
    app_handle: AppHandle<R>,
    data: String,
    id: String,
) -> Result<(), String> {
    let user_dir = crate::get_user_dir();
    if user_dir.is_none() {
        return Err("miss user dir".into());
    }
    let user_id = get_user_id(app_handle.clone()).await;
    if user_id == "" {
        return Err("must login".into());
    }
    let file_name = format!("data_view_nodes_{}.json", id);
    let mut file_path = std::path::PathBuf::from(user_dir.unwrap());
    file_path.push(user_id);
    if !file_path.exists() {
        let result = fs::create_dir_all(&file_path).await;
        if result.is_err() {
            return Err(result.err().unwrap().to_string());
        }
    }
    file_path.push(file_name);

    let f = fs::File::create(file_path).await;
    if f.is_err() {
        return Err(f.err().unwrap().to_string());
    }
    let mut f = f.unwrap();
    let result = f.write_all(data.as_bytes()).await;
    if result.is_err() {
        return Err(result.err().unwrap().to_string());
    }
    return Ok(());
}

async fn load_view_list<R: Runtime>(
    app_handle: AppHandle<R>,
) -> Result<Vec<UserDataViewInfo>, String> {
    let user_dir = crate::get_user_dir();
    if user_dir.is_none() {
        return Err("miss user dir".into());
    }
    let user_id = get_user_id(app_handle.clone()).await;
    if user_id == "" {
        return Err("must login".into());
    }
    let mut file_path = std::path::PathBuf::from(user_dir.unwrap());
    file_path.push(user_id);
    file_path.push("data_view.json");

    if !file_path.exists() {
        return Ok(Vec::new());
    }

    let f = fs::File::open(file_path).await;
    if f.is_err() {
        return Err(f.err().unwrap().to_string());
    }
    let mut f = f.unwrap();
    let mut data = Vec::new();
    let result = f.read_to_end(&mut data).await;
    if result.is_err() {
        return Err(result.err().unwrap().to_string());
    }
    let json_str = String::from_utf8(data);
    if json_str.is_err() {
        return Err(json_str.err().unwrap().to_string());
    }
    let json_str = json_str.unwrap();
    let view_list = serde_json::from_str(&json_str);
    if view_list.is_err() {
        return Err(view_list.err().unwrap().to_string());
    }
    let view_list = view_list.unwrap();
    return Ok(view_list);
}

async fn save_view_list<R: Runtime>(
    app_handle: AppHandle<R>,
    view_list: Vec<UserDataViewInfo>,
) -> Result<(), String> {
    let user_dir = crate::get_user_dir();
    if user_dir.is_none() {
        return Err("miss user dir".into());
    }
    let user_id = get_user_id(app_handle.clone()).await;
    if user_id == "" {
        return Err("must login".into());
    }
    let mut file_path = std::path::PathBuf::from(user_dir.unwrap());
    file_path.push(user_id);
    if !file_path.exists() {
        let result = fs::create_dir_all(&file_path).await;
        if result.is_err() {
            return Err(result.err().unwrap().to_string());
        }
    }
    file_path.push("data_view.json");

    let json_str = serde_json::to_string(&view_list);
    if json_str.is_err() {
        return Err(json_str.err().unwrap().to_string());
    }
    let json_str = json_str.unwrap();

    let f = fs::File::create(file_path).await;
    if f.is_err() {
        return Err(f.err().unwrap().to_string());
    }
    let mut f = f.unwrap();
    let result = f.write_all(json_str.as_bytes()).await;
    if result.is_err() {
        return Err(result.err().unwrap().to_string());
    }

    return Ok(());
}

#[tauri::command]
async fn add_view<R: Runtime>(
    app_handle: AppHandle<R>,
    view: UserDataViewInfo,
) -> Result<(), String> {
    let tmp_list = load_view_list(app_handle.clone()).await;
    if tmp_list.is_err() {
        return Err(tmp_list.err().unwrap());
    }
    let tmp_list = tmp_list.unwrap();
    for cmp_view in &tmp_list {
        if cmp_view.id == view.id {
            return Ok(());
        }
    }

    let mut view_list = vec![view];
    view_list.extend(tmp_list);
    return save_view_list(app_handle, view_list).await;
}

#[tauri::command]
async fn update_view<R: Runtime>(
    app_handle: AppHandle<R>,
    view: UserDataViewInfo,
) -> Result<(), String> {
    let tmp_list = load_view_list(app_handle.clone()).await;
    if tmp_list.is_err() {
        return Err(tmp_list.err().unwrap());
    }
    let tmp_list = tmp_list.unwrap();
    let mut new_view_list = Vec::new();
    for cmp_view in &tmp_list {
        let view_id = view.id.clone();
        if cmp_view.id == view_id {
            new_view_list.push(view.clone());
        } else {
            new_view_list.push(cmp_view.clone());
        }
    }

    return save_view_list(app_handle, new_view_list).await;
}

#[tauri::command]
async fn remove_view<R: Runtime>(app_handle: AppHandle<R>, id: String) -> Result<(), String> {
    let tmp_list = load_view_list(app_handle.clone()).await;
    if tmp_list.is_err() {
        return Err(tmp_list.err().unwrap());
    }
    let tmp_list = tmp_list.unwrap();
    let mut new_view_list = Vec::new();
    for cmp_view in &tmp_list {
        if cmp_view.id != id {
            new_view_list.push(cmp_view.clone());
        }
    }

    let res = save_view_list(app_handle.clone(), new_view_list).await;
    if res.is_err() {
        return Err(res.err().unwrap());
    }
    //删除节点文件
    let user_dir = crate::get_user_dir();
    if user_dir.is_none() {
        return Err("miss user dir".into());
    }
    let user_id = get_user_id(app_handle.clone()).await;
    if user_id == "" {
        return Err("must login".into());
    }
    let file_name = format!("data_view_nodes_{}.json", id);
    let mut file_path = std::path::PathBuf::from(user_dir.unwrap());
    file_path.push(user_id);
    file_path.push(file_name);

    if file_path.exists() {
        let res = fs::remove_file(file_path).await;
        if res.is_err() {
            return Err(res.err().unwrap().to_string());
        }
    }

    return Ok(());
}

#[tauri::command]
async fn list_view<R: Runtime>(app_handle: AppHandle<R>) -> Result<Vec<UserDataViewInfo>, String> {
    return load_view_list(app_handle).await;
}

pub struct UserDataViewApiPlugin<R: Runtime> {
    invoke_handler: Box<dyn Fn(Invoke<R>) + Send + Sync + 'static>,
}

impl<R: Runtime> UserDataViewApiPlugin<R> {
    pub fn new() -> Self {
        Self {
            invoke_handler: Box::new(tauri::generate_handler![
                add_source,
                update_source,
                remove_source,
                list_source,
                load_node_data,
                save_node_data,
                add_view,
                update_view,
                remove_view,
                list_view,
            ]),
        }
    }
}

impl<R: Runtime> Plugin<R> for UserDataViewApiPlugin<R> {
    fn name(&self) -> &'static str {
        "user_dataview_api"
    }
    fn initialization_script(&self) -> Option<String> {
        None
    }

    fn initialize(&mut self, app: &AppHandle<R>, _config: serde_json::Value) -> PluginResult<()> {
        app.manage(FileLock(Default::default()));
        Ok(())
    }

    fn created(&mut self, _window: Window<R>) {}

    fn on_page_load(&mut self, _window: Window<R>, _payload: PageLoadPayload) {}

    fn extend_api(&mut self, message: Invoke<R>) {
        (self.invoke_handler)(message)
    }
}
